package set

import "duanju-mq/pkg/container/list"

type Set[T comparable] map[T]struct{}

func (s *Set[T]) Len() int {
	return len(*s)
}

func (s *Set[T]) Add(v ...T) {
	for _, k := range v {
		(*s)[k] = struct{}{}
	}
}

func (s *Set[T]) Remove(v ...T) {
	for _, k := range v {
		delete(*s, k)
	}
}

func (s *Set[T]) Contains(v T) bool {
	_, ok := (*s)[v]
	return ok
}

func (s *Set[T]) ToList() list.List[T] {
	l := make(list.List[T], 0, len(*s))
	for v := range *s {
		l.Append(v)
	}
	return l
}

// Func 函数式方法，不影响原集合，链式调用
func (s *Set[T]) Func() *Func[T] { return &Func[T]{*s} }

type Func[T comparable] struct {
	s Set[T]
}

func (f *Func[T]) ToSet() Set[T] { return f.s }

func (f *Func[T]) Clone() *Func[T] {
	nf := &Func[T]{s: make(Set[T], f.s.Len())}
	for v := range f.s {
		nf.s.Add(v)
	}
	return nf
}

// Every 检查是否每一项都满足条件
func (f *Func[T]) Every(fn func(T) bool) bool {
	for v := range f.s {
		if !fn(v) {
			return false
		}
	}
	return true
}

// Any 检查任意一项满足条件
func (f *Func[T]) Any(fn func(T) bool) bool {
	for v := range f.s {
		if fn(v) {
			return true
		}
	}
	return false
}

// ForEach 遍历
func (f *Func[T]) ForEach(fn func(T)) *Func[T] {
	for v := range f.s {
		fn(v)
	}
	return f
}

// Where 返回所有满足条件元素的新集合
func (f *Func[T]) Where(fn func(T) bool) *Func[T] {
	nf := &Func[T]{s: Set[T]{}}
	for v := range f.s {
		if fn(v) {
			nf.s.Add(v)
		}
	}
	return nf
}

func (f *Func[T]) Inter(ss ...Set[T]) *Func[T] {
	nf := &Func[T]{s: Set[T]{}}
	for v := range f.s {
		ok := true
		for _, s := range ss {
			if !s.Contains(v) {
				ok = false
				break
			}
		}
		if ok {
			nf.s.Add(v)
		}
	}
	return nf
}

func (f *Func[T]) Union(ss ...Set[T]) *Func[T] {
	nf := &Func[T]{s: Set[T]{}}
	for v := range f.s {
		nf.s.Add(v)
	}
	for _, s := range ss {
		for v := range s {
			nf.s.Add(v)
		}
	}
	return nf
}

func (f *Func[T]) Diff(ss ...Set[T]) *Func[T] {
	nf := &Func[T]{s: Set[T]{}}
	for v := range f.s {
		ok := true
		for _, s := range ss {
			if s.Contains(v) {
				ok = false
				break
			}
		}
		if ok {
			nf.s.Add(v)
		}
	}
	return nf
}
